package ch.il07.converter.business.impl;

import java.util.LinkedList;
import java.util.List;

import ch.il07.converter.business.UnitAction;
import ch.il07.converter.persistence.Persistence;
import ch.il07.converter.persistence.PersistenceHelper;
import ch.il07.converter.persistence.entities.Measurand;
import ch.il07.converter.persistence.entities.Unit;
import ch.il07.converter.persistence.entities.User;
import ch.il07.converter.persistence.exceptions.PersistenceException;
import ch.il07.utils.logging.Level;
import ch.il07.utils.logging.Logger;
import ch.il07.utils.logging.LoggerHelper;

public class UnitActionImpl implements UnitAction {

	private final static Logger LOGGER = LoggerHelper.getLogger();

	/**
	 * author: Oliver
	 */
	@Override
	public boolean addPrivateUnit(String measurandName, String owner,
			String name, Double unitDivisor, Double unitShift, String symbol) {
		User user;
		if (!ServiceHelper.parameterListNotNull(measurandName, name,
				unitDivisor, unitShift)) {
			throw new IllegalArgumentException(
					"You HAVE TO give in measurand, name, unitDivisor AND unitShift!");
		}

		try {
			Persistence persistence = PersistenceHelper.getPersistence();
			if ((user = persistence.getUser(owner)) == null) {
				LOGGER.error("user [" + owner + "] does not exist");
				return false;

			}
			Measurand m = persistence.getMeasurand(measurandName, owner);
			if (m == null) {
				m = persistence.getMeasurand(measurandName, null);
			}
			Unit u = new Unit(name, unitDivisor, unitShift);
			u.setOwner(user);
			u.setSymbol(symbol);
			persistence.addUnit(m, u);
			m.addUnit(u);
			return true;
		} catch (PersistenceException e) {
			LOGGER.error("error invoking persistence in addPrivateUnit", e);
			return false;
		}
	}

	/**
	 * author: Oliver
	 */
	@Override
	public boolean addPublicUnit(String measurand, String name,
			Double unitDivisor, Double unitShift, String symbol) {
		return addPrivateUnit(measurand, null, name, unitDivisor, unitShift, symbol);
	}

	/**
	 * author: Oliver
	 */
	@Override
	public List<Unit> getPrivateUnitsMeasurand(String measurandName,
			String username) {
		if (measurandName == null) {
			throw new NullPointerException("measurand name must not be null");
		} else if (username == null) {
			throw new NullPointerException("username must not be null");
		}
		try {
			Persistence persistence = PersistenceHelper.getPersistence();
			Measurand m = persistence.getMeasurand(measurandName, username);
			// measurand is either public or not available
			if (m == null) {
				if (LOGGER.isLoggable(Level.INFO)) {
					LOGGER.info("private measurand [" + measurandName
							+ "] for user [" + username
							+ "] not found. now trying to find public");
				}
				m = persistence.getMeasurand(measurandName, null);
				if (m == null) {
					if (LOGGER.isLoggable(Level.WARNING)) {
						LOGGER.warning("public measurand [" + measurandName
								+ "] not found");
					}
					return null;
				}
			}
			List<Unit> units = m.getUnits();
			units.add(m.getDefaultUnit());
			if (LOGGER.isLoggable(Level.FINE)) {
				LOGGER.fine("[" + units.size() + "] units for measurand ["
						+ measurandName
						+ "] found at all. now trying to filter private");
			}
			return filterPrivateUnits(units, username);
		} catch (PersistenceException e) {
			LOGGER.error("error while invoking persistence", e);
			return null;
		}
	}

	private static List<Unit> filterPrivateUnits(List<Unit> units,
			String username) {
		List<Unit> filtered = new LinkedList<Unit>();
		for (Unit unit : units) {
			User user = unit.getOwner();
			if (user != null) {
				if (user.getUsername().equals(username)) {
					filtered.add(unit);
				}
			}
		}
		return filtered;
	}

	/**
	 * author: Oliver
	 */
	@Override
	public List<Unit> getPublicUnitsMeasurand(String measurandName) {
		if (measurandName == null) {
			throw new NullPointerException("measurand name must not be null");
		}
		try {
			Persistence persistence = PersistenceHelper.getPersistence();
			Measurand m = persistence.getMeasurand(measurandName, null);

			// private measurand?
			if (m == null) {
				if (LOGGER.isLoggable(Level.INFO)) {
					LOGGER.info("measurand [" + measurandName
							+ "] seems to be private, return empty list");
				}
				return new LinkedList<Unit>();
			}

			List<Unit> units = m.getUnits();
			units.add(m.getDefaultUnit());
			if (LOGGER.isLoggable(Level.FINE)) {
				LOGGER.fine("[" + units.size()
						+ "] units found for measurand [" + measurandName
						+ "]. now filtering public units");
			}

			return filterPublicUnits(units);
		} catch (PersistenceException e) {
			LOGGER.error("error while invoking persistence", e);
			return null;
		}
	}

	private List<Unit> filterPublicUnits(List<Unit> units) {
		List<Unit> filtered = new LinkedList<Unit>();

		for (Unit unit : units) {
			if (unit.getOwner() == null) {
				filtered.add(unit);
			}
		}
		return filtered;
	}

	@Override
	public boolean editUnit(String username, Integer unitID, String unitName,
			Double factor, Double shift, String symbol) {
		if (unitID == null) {
			throw new NullPointerException("unitID must not be null");
		}
		try {
			Persistence persistence = PersistenceHelper.getPersistence();
			List<Measurand> measurands;
			if (username == null) {
				measurands = persistence.getMeasurands();
			} else {
				measurands = persistence.getMeasurands(username);
			}
			for (Measurand measurand : measurands) {
				for (Unit unit : measurand.getUnits()) {
					if (unit.getUnitId() == unitID) {
						updateUnit(unitName, factor, shift, symbol, unit);
						persistence.updateUnit(measurand, unit);
						return true;
					}
				}
				// only possible to update name and symbol on defaultUnit
				Unit defaultUnit = measurand.getDefaultUnit();
				if (defaultUnit.getUnitId() == unitID) {
					updateUnit(unitName, null, null, symbol, defaultUnit);
					persistence.updateUnit(measurand, defaultUnit);
					return true;
				}
			}
			LOGGER.warning("editUnit: unit with id [" + unitID + "] and user ["
					+ username + "] not found");
			return false;
		} catch (PersistenceException e) {
			LOGGER.error("error while invoking persistence", e);
			return false;
		}
	}

	private void updateUnit(String unitName, Double factor, Double shift,
			String symbol, Unit unit) {
		String oldName = unit.getName();
		if (unitName != null) {
			unit.setName(unitName);
			if (LOGGER.isLoggable(Level.FINE)) {
				LOGGER.fine("update name for unit [" + oldName + "] to ["
						+ unitName + "]");
			}
		}
		if (factor != null) {
			unit.setDivisor(factor);
			if (LOGGER.isLoggable(Level.FINE)) {
				LOGGER.fine("update divisor for unit [" + oldName + "] to ["
						+ factor + "]");
			}
		}
		if (shift != null) {
			unit.setShift(shift);
			if (LOGGER.isLoggable(Level.FINE)) {
				LOGGER.fine("update shift for unit [" + oldName + "] to ["
						+ shift + "]");
			}
		}
		if (symbol != null) {
			unit.setSymbol(symbol);
			if (LOGGER.isLoggable(Level.FINE)) {
				LOGGER.fine("update symbol for unit [" + oldName + "] to ["
						+ symbol + "]");
			}
		}
	}

	@Override
	public boolean proposeUnitForPublic(Integer unitID) {
		if (unitID == null) {
			throw new NullPointerException("unitID must not be null");
		}
		try {
			Persistence persistence = PersistenceHelper.getPersistence();
			List<Measurand> measurands = persistence.getMeasurands();
			for (Measurand measurand : measurands) {
				unitLoop: for (Unit unit : measurand.getUnits()) {
					if (unit.getOwner() == null) {
						// already public
						continue unitLoop;
					} else if (unit.getUnitId() == unitID) {
						persistence.nominate(unit.getOwner(), measurand, unit);
						return true;
					}
				}

				// defaultUnit
				Unit defaultUnit = measurand.getDefaultUnit();
				if (defaultUnit.getUnitId() == unitID) {
					persistence.nominate(defaultUnit.getOwner(), measurand,
							defaultUnit);
					return true;
				}
			}
			LOGGER.warning("proposeUnitForPublic: unit with id [" + unitID
					+ "] not found");
			return false;
		} catch (PersistenceException e) {
			LOGGER.error("error while invoking persistence", e);
			return false;
		}
	}

	@Override
	public boolean deleteUnit(String username, Integer unitID) {
		if (unitID == null) {
			throw new NullPointerException("unitID must not be null");
		}
		try {
			Persistence persistence = PersistenceHelper.getPersistence();
			List<Measurand> measurands;
			if (username == null) {
				measurands = persistence.getMeasurands();
			} else {
				measurands = persistence.getMeasurands(username);
			}
			for (Measurand measurand : measurands) {
				for (Unit unit : measurand.getUnits()) {
					if (unit.getUnitId() == unitID) {
						persistence.deleteUnit(measurand, unit);
						return true;
					}
				}
			}
			LOGGER.warning("deleteUnit: unit with id [" + unitID
					+ "] and user [" + username + "] not found");
			return false;
		} catch (PersistenceException e) {
			LOGGER.error("error while invoking persistence", e);
			return false;
		}
	}
}